<?php
// +----------------------------------------------------------------------
// | Workman and Gateway-worker
// +----------------------------------------------------------------------
// | Copyright (c) 2015-2025 http://www.yicmf.com, All rights reserved.
// +----------------------------------------------------------------------
// | Author: 微尘 <yicmf@qq.com>
// +----------------------------------------------------------------------

namespace yicmf\worker;

use think\facade\App;
use think\facade\Cache;
use think\facade\Db;
use think\facade\Log;
use Workerman\Timer;
use Workerman\Worker as WorkermanWorker;

/**
 * Worker 命令行服务类
 */
abstract class Plan
{
    protected $worker;
    protected $option = [];
    protected $event = ['onWorkerStart', 'onClose', 'onError'];

    protected static $log_channel = 'task';
    protected static $time_rang = ['sec' => 1, 'min' => 60, 'hour' => 3600, 'day' => 86400];

    /**
     * 架构函数
     * @access public
     */
    public function __construct($option = [])
    {

        unset($option['handler']);
        unset($option['enable']);
        $this->option = array_merge($this->option, $option);
        // 实例化 Websocket 服务
        $this->worker = new WorkermanWorker();
        // 避免pid混乱
        $this->option['pidFile'] = runtime_path() . 'plan.pid';
        $this->option['logFile'] = runtime_path() . 'plan_worker.log';

        // 设置服务器参数
        if (!empty($this->option)) {
            foreach ($this->option as $name => $val) {
                if ($val) {
                    if (in_array($name, ['stdoutFile', 'daemonize', 'pidFile', 'logFile'])) {
                        WorkermanWorker::${$name} = $val;
                    } else {
                        $this->worker->$name = $val;
                    }
                }

            }
        }
        // 设置回调
        foreach ($this->event as $event) {
            if (method_exists($this, $event)) {
                $this->worker->$event = [$this, $event];
            }
        }

    }

    protected static function init()
    {
        Db::listen(function ($sql, $runtime, $master) {
            // 进行监听处理
            Log::channel(self::$log_channel)->record($sql . ' [ RunTime:' . $runtime . 's ]');
        });
    }


    public function __set($name, $value)
    {
        $this->worker->$name = $value;
    }

    public function __call($method, $args)
    {
        call_user_func_array([$this->worker, $method], $args);
    }

    /**
     * onWorkerStart 事件回调
     * 当businessWorker进程启动时触发。每个进程生命周期内都只会触发一次
     *
     * @access public
     * @param WorkermanWorker $worker
     * @return void
     */
    public static function onWorkerStart(WorkermanWorker $worker)
    {
        $app = new Application();
        $app->initialize();
        // 初始化
        static::init();
        // 1秒一次轮询
        Timer::add(1, function () use ($worker) {
//            $this->beginTime = microtime(true);
//            $this->beginMem  = memory_get_usage();
            $_SERVER['begin_time'] = microtime(true);
            if (DIRECTORY_SEPARATOR === '\\') {
                //windows
                $id = null;
            } else {
                $id = $worker->id;
            }
            $reflection = new \ReflectionClass(static::class);
            $methods = $reflection->getMethods();
            try {
                foreach ($methods as $method) {
                    $mName = $method->name;
                    if ($method->class === static::class && $mName != 'init' && (false === strpos($mName, 'on') || 0 !==strpos($mName, 'on'))) { // 检查方法是否定义在子类中
                        if ($mName == 'run') {
                            static::run();
                        } elseif (strpos($mName, '_')) {
                            $range = explode('_', $mName);
                            if (!empty($range[1])) {
                                self::runByCache($mName, $range, $id);
                            }
                        } else {
                            Log::channel(self::$log_channel)->record('未知的计划任务' . $mName);
                        }
                    }
                }
                $data['code'] = 0;
                $data['message'] = 'ok';
            } catch (\Exception $e) {
                Log::channel(self::$log_channel)->write("\r\n" . 'Exception Message:' . "\r\n" . $e->getMessage() . "\r\n" . 'file:' . $e->getFile() . 'line:' . $e->getLine() . "\r\n" . 'trace:' . $e->getTraceAsString(), 'error');
                foreach ($methods as $method) {
                    if ($method->class === static::class && $method->name == 'onException') { // 检查方法是否定义在子类中
                        static::onException($e);
                    }
                }
            }
            $re = Log::channel(self::$log_channel)->getLog();
            if (count($re)) {
                Log::channel(self::$log_channel)->save();
            }
        });
    }

    protected static function runByCache($mName, $range, $id = null)
    {
        if (isset(self::$time_rang[$range[0]])) {
            if (!Cache::has('workman_plan_' . $mName)) {
                Cache::set('workman_plan_' . $mName, 1, 100);
                Log::channel(self::$log_channel)->record('计划任务' . $id . 'go' . $mName);
                static::$mName();
                $t = max($range[1] * self::$time_rang[$range[0]] - time() % ($range[1] * self::$time_rang[$range[0]]),1);
                Log::channel(self::$log_channel)->record('计划任务' . $id . 'go' . $mName.'_'.$t);
                Cache::set('workman_plan_' . $mName, 1, $t);
            }
        } else if ($range[0] == 'week') {
            // 周计划为指定周几一直执行
            if ($range[1] == date('N')) {
                if (!Cache::has('workman_plan_' . $mName)) {
                    Cache::set('workman_plan_' . $mName, 1, 100);
                    Log::channel(self::$log_channel)->record('计划任务' . $id . 'go' . $mName);
                    static::$mName();
                    Cache::set('workman_plan_' . $mName, 1, (int)(isset($range[2]) ? $range[2] : 1));
                }
            }
        } else if ($range[0] == 'month') {
            //Log::channel(self::$log_channel)->record('计划任务' . $id . 'go' . $mName);
            if (date('j') == $range[1]) {
                if (!Cache::has('workman_plan_' . $mName)) {
                    Cache::set('workman_plan_' . $mName, 1, 100);
                    Log::channel(self::$log_channel)->record('计划任务' . $id . 'go' . $mName);
                    static::$mName();
                    Cache::set('workman_plan_' . $mName, 1, (int)(isset($range[2]) ? $range[2] : 1));
                }
            }
        } else {
            Log::channel(self::$log_channel)->record('未执行的计划任务' . $mName);
        }
    }

    /**
     * 每秒
     */
    public static function run($id = null)
    {
        Log::channel(self::$log_channel)->write('run');
    }

    /**
     * onClose 事件回调
     * @return void
     */
    public static function onClose()
    {

        Log::channel(self::$log_channel)->write('onClose');
    }

    /**
     * onClose 事件回调
     * @return void
     */
    protected static function _log($info)
    {

        Log::channel(self::$log_channel)->info($info);
    }
}
